using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using TestCases.CompareAssert.Core.Comparers;
using TestCases.CompareAssert.Core.Interfaces;
using TestCases.CompareAssert.Interfaces;
using TestCases.CompareAssert.Matchers;
using TestCases.CompareAssert.Settings;
using TestCases.CompareAssert.Settings.Interfaces;
using TestCases.Utils;

namespace TestCases.CompareAssert
{
    public class CustomizableAssertion: ICustomizableAssertion
    {
        private readonly ICompareAssertion _assertion;
        private readonly CompareSetup _setup;

        public CustomizableAssertion(IAssertionOptions options)
        {
            _setup = new CompareSetup();
            _assertion = new CompareAssertion(options, new CustomizableComparer(_setup));
        }
        
        public CustomizableAssertion() : this(CAssertOptions.DefaultInstance)
        {
        }
        
        public ICompareAssertion That
        {
            get { return _assertion; }
        }

        public ICompareSetup ForContextMatch(IObjectMatcher<ICompareContext> matcher)
        {
            return _setup.GetSetupFor(matcher);
        }

        private ICompareSetup ForObjectMatch<TFieldType>(
            Func<IComparerObject, TFieldType> fieldSelector, 
            IObjectMatcher<TFieldType> fieldMatcher) 
            where TFieldType : class
        {
            return _setup.GetSetupFor(new ContextObjectMatcher<TFieldType>(fieldSelector, fieldMatcher));
        }

        public ICompareSetup ForSpecificType<T>()
        {
            return ForSpecificType(typeof(T));
        }

        public ICompareSetup ForSpecificType(Type type)
        {
            return ForObjectMatch(x => x.Object.GetSafeType(), new ObjectMatcher<Type>(type));
        }

        public ICompareSetup ForSpecificTypes(IEnumerable<Type> types)
        {
            return ForObjectMatch(x => x.Object.GetSafeType(), new ObjectMatcher<Type>(types));
        }

        public ICompareSetup ForInstanceOf<T>()
        {
            return ForInstanceOf(typeof(T));
        }

        public ICompareSetup ForInstanceOf(Type type)
        {
            return ForObjectMatch(x => x.Object.GetSafeType(), new InstanceOfMatcher(type));
        }

        public ICompareSetup ForInstancesOf(IEnumerable<Type> types)
        {
            return ForObjectMatch(x => x.Object.GetSafeType(), new InstanceOfMatcher(types));
        }

        public ICompareSetup ForTypeMatch(Predicate<Type> predicate)
        {
            return ForObjectMatch(x => x.Object.GetSafeType(), new ObjectMatcher<Type>(predicate));
        }

        public ICompareSetup ForSpecificPath(string path)
        {
            return ForObjectMatch(x => x.Path, new StringMatcher(path));
        }

        public ICompareSetup ForSpecificPaths(IEnumerable<string> paths)
        {
            return ForObjectMatch(x => x.Path, new StringMatcher(paths));
        }

        public ICompareSetup ForPathMatch(string regex)
        {
            return ForObjectMatch(x => x.Path, new StringMatcher(new Regex(regex)));
        }

        public ICompareSetup ForPathMatch(Predicate<string> predicate)
        {
            return ForObjectMatch(x => x.Path, new StringMatcher(predicate));
        }

        public ICompareSetup ForSpecificValue(object value)
        {
            return ForObjectMatch(x => x.Object, new ObjectMatcher<object>(value));
        }

        public ICompareSetup ForSpecificValues(IEnumerable<object> values)
        {
            return ForObjectMatch(x => x.Object, new ObjectMatcher<object>(values));            
        }

        public ICompareSetup ForValueMatch(Predicate<object> predicate)
        {
            return ForObjectMatch(x => x.Object, new ObjectMatcher<object>(predicate));
        }

        public ICompareSetup Setup
        {
            get { return _setup; }
        }

        public void Reset()
        {
            _setup.FullReset();
        }
    }
}